﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using FlickrSL;
using FlickrSL.Channels;
using FlickrSL.Data;
using SilverlightRestServices;

namespace FlickrSLTest
{
    public partial class MainPage : UserControl
    {
        private Flickr m_Channel;
        private ChannelFactory<Flickr> m_ChannelFactory;
        private Boolean m_Cancel = false;
        private Int32 m_TestsCompleted;
        private TreeViewItem m_CurrentItem;

        private List<MethodInfo> m_TestMethods;

        public MainPage()
        {
            this.InitializeComponent();
            this.BuildChannel();

            FlickrSecurity.ApiKey = "<INSERT YOUR API KEY HERE FOR TESTING>";
            this.m_TestMethods = this.GetType().GetMethods().Where(mi => mi.Name.EndsWith("Test")).ToList();
        }

        private void BuildChannel()
        {
            this.m_ChannelFactory = new ChannelFactory<Flickr>(new RestBinding(), new EndpointAddress("http://api.flickr.com/"));
            this.m_ChannelFactory.Open();

            this.m_Channel = this.m_ChannelFactory.CreateChannel();
        }

        private void BeginTestButton_Click(Object sender, RoutedEventArgs e)
        {
            this.m_Cancel = false;

            this.TestReportTreeView.Items.Clear();
            this.TestProgress.Value = this.m_TestsCompleted = 0;
            this.TestProgress.Maximum = this.m_TestMethods.Count;
            this.TestProgress.Foreground = new SolidColorBrush(Color.FromArgb(255, 0, 255, 0));

            this.BeginTestButton.Visibility = Visibility.Collapsed;
            this.CancelTestButton.Visibility = Visibility.Visible;

            RunNextTest_();
        }

        private void RunNextTest_()
        {
            if (!this.m_Cancel && this.m_TestsCompleted < this.m_TestMethods.Count)
            {
                this.m_CurrentItem = new TreeViewItem();
                this.m_CurrentItem.Header = this.m_TestMethods[this.m_TestsCompleted].Name + ": Running";
                this.TestReportTreeView.Items.Add(this.m_CurrentItem);

                this.m_TestMethods[this.m_TestsCompleted].Invoke(this, new Object[] { this.m_Channel });
            }
            else
            {
                this.BeginTestButton.Visibility = Visibility.Visible;
                this.CancelTestButton.Visibility = Visibility.Collapsed;
            }
        }

        private void OnError(Exception err)
        {
            if (this.m_ChannelFactory.State == CommunicationState.Faulted)
            {
                this.BuildChannel();
            }

            this.m_CurrentItem.Header = this.m_TestMethods[this.m_TestsCompleted].Name + ": Failed";
            this.m_CurrentItem.Foreground = new SolidColorBrush(Colors.Red);

            TreeViewItem resultErr = new TreeViewItem();
            resultErr.Header = "Exception: " + err.GetType().Name;

            TreeViewItem resultErrMessage = new TreeViewItem();
            resultErrMessage.Header = "Message: " + err.Message;

            TreeViewItem resultErrStackTrace = new TreeViewItem();
            resultErrStackTrace.Header = "Stack Trace: " + err.StackTrace;

            this.m_CurrentItem.Items.Add(resultErr);
            this.m_CurrentItem.Items.Add(resultErrMessage);
            this.m_CurrentItem.Items.Add(resultErrStackTrace);

            this.m_TestsCompleted++;
            this.TestProgress.Value = this.m_TestsCompleted;
            this.TestProgress.Foreground = new SolidColorBrush(Colors.Red);
            this.RunNextTest_();
        }

        private void OnSuccess()
        {
            this.m_CurrentItem.Header = this.m_TestMethods[this.m_TestsCompleted].Name + ": Succeeded";
            this.m_TestsCompleted++;
            this.TestProgress.Value = this.m_TestsCompleted;
            this.RunNextTest_();
        }

        #region Asserts

        public void Assert(Boolean condition, String message)
        {
            if (!condition)
            {
                throw new Exception(message);
            }
        }

        public void AssertNotNull(Object obj, String message)
        {
            if (obj == null)
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(String expected, String actual, String message)
        {
            AssertEqual(expected, actual, false, message);
        }

        public void AssertEqual(String expected, String actual, Boolean ignoreCase, String message)
        {
            if (!String.Equals(expected, actual, ignoreCase ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal))
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(Enum expected, Enum actual, String message)
        {
            if (!Enum.Equals(expected, actual))
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(Int32 expected, Int32 actual, String message)
        {
            if (expected != actual)
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(Int64 expected, Int64 actual, String message)
        {
            if (expected != actual)
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(Double expected, Double actual, String message)
        {
            if (expected != actual)
            {
                throw new Exception(message);
            }
        }

        public void AssertEqual(DateTime expected, DateTime actual, String message)
        {
            if (expected != actual)
            {
                throw new Exception(message);
            }
        }

        #endregion

        public void EchoTest(Flickr channel)
        {
            channel.BeginTest_Echo(new EchoParameterCollection("foo", "bar", "bas", "42"),
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<EchoResponse> response = channel.EndTest_Echo(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        Assert(response.Element.Members.ContainsKey("foo"), "Key Foo Missing");
                        Assert(response.Element.Members.ContainsKey("bas"), "Key Bas Missing");
                        AssertEqual("bar", response.Element.Members["foo"], true, "Value of Foo does not match");
                        AssertEqual(42, Int32.Parse(response.Element.Members["bas"]), "Value of Bas does not match");
                        
                        Dispatcher.BeginInvoke(OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(OnError), err);
                    }
                }, null);
        }

        public void PhotoGetInfoTest(Flickr channel)
        {
            channel.BeginPhotos_GetPhotoInfo(4627858959,
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<PhotoInfo> response = channel.EndPhotos_GetPhotoInfo(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        PhotoInfo photo = response.Element;

                        AssertEqual(4627858959, photo.Id, "Photo Id Does Not Match");
                        AssertEqual("a93669858f", photo.Secret, "Secret Does Not Match");
                        Assert(photo.DatePosted.HasValue, "Date Posted Missing");
                        AssertEqual(new DateTime(2010, 05, 21, 20, 23, 15, DateTimeKind.Local).ToUniversalTime(), photo.DatePosted.Value, "Date Posted Does Not Match");
                        Assert(!photo.IsFavorite, "IsFavorite Value Does Not Match");
                        AssertEqual(1, photo.LicenseId, "License Does Not Match");
                        AssertEqual(0, photo.Rotation, "Rotation Does Not Match");
                        AssertEqual("7e8d905b1e", photo.OriginalSecret, "Original Secret Does Not Match");
                        AssertEqual("jpg", photo.OriginalFormat, "Original Format Does Not Match");
                        Assert(photo.ViewCount >= 9, "View Count Incorrect");
                        AssertEqual("photo", photo.MediaType, "Media Type Does Not Match");

                        AssertEqual("Tiny Car", photo.Title, "Title Does Not Match");
                        Assert(String.IsNullOrEmpty(photo.Description), "Description Does Not Match");
                        Assert(photo.Visibility.IsPublic, "Photo Visibility is Incorrect, Should Be Public");
                        Assert(!photo.Visibility.IsFriend, "Photo Visibility is Incorrect, Should Not Be Friend");
                        Assert(!photo.Visibility.IsFamily, "Photo Visibility is Incorrect, Should Not Be Family");

                        AssertEqual(photo.DatePosted.GetValueOrDefault(), photo.Dates.DatePosted.GetValueOrDefault(), "Date Posted Does Not Match");
                        Assert(photo.Dates.LastUpdate >= photo.Dates.DatePosted, "Last Update is Incorrect");
                        AssertEqual(new DateTime(2010, 03, 05, 20, 55, 23, DateTimeKind.Unspecified), photo.Dates.DateTaken.Value, "Date Taken Does Not Match");
                        AssertEqual(DateGranularity.Exact, photo.Dates.DateTakenGranularity, "Date Taken Granularity Does Not Match");

                        Assert(!photo.Editability.CanAddMetadata, "Editability.CanAddMetadata is Incorrect");
                        Assert(!photo.Editability.CanComment, "Editability.CanComment is Incorrect");

                        Assert(photo.Usage.CanDownload, "Usage.CanDownload is Incorrect");
                        Assert(!photo.Usage.CanBlog, "Usage.CanBlog is Incorrect");
                        Assert(!photo.Usage.CanPrint, "Usage.CanPrint is Incorrect");
                        Assert(!photo.Usage.CanShare, "Usage.CanShare is Incorrect");

                        AssertEqual(4, photo.Tags.Count, "Incorrect Number of Tags");

                        CheckTag(photo.Tags[0], "2689858-4627858959-5566", "74579776@N00", "Album", "album");
                        CheckTag(photo.Tags[1], "2689858-4627858959-8512", "74579776@N00", "Australia", "australia");
                        CheckTag(photo.Tags[2], "2689858-4627858959-17567", "74579776@N00", "BMW", "bmw");
                        CheckTag(photo.Tags[3], "2689858-4627858959-126118", "74579776@N00", "Isetta", "isetta");

                        Assert(photo.Location != null, "Location is Missing");
                        AssertEqual(-33.883286, photo.Location.Latitude, "Latitude Does Not Match");
                        AssertEqual(151.272811, photo.Location.Longitude, "Longitude Does Not Match");
                        AssertEqual(LocationAccuracy.Street, photo.Location.Accuracy, "Location Accuracy Does Not Match");
                        AssertEqual(0, photo.Location.Context, "Location Context Does Not Match");
                        AssertEqual("3sApDmabA5vusiBh9g", photo.Location.PlaceId, "Location Place Id Does Not Match");
                        AssertEqual(22721174, photo.Location.WhereOnEarthId, "Location Where on Earth Id Does Not Match");

                        AssertEqual(4, photo.Location.Places.Count, "Incorrect Number of Location Places");

                        Assert(photo.Location.Places.ContainsKey(PlaceType.Neighbourhood), "Neighbourhood Missing");
                        Assert(photo.Location.Places.ContainsKey(PlaceType.Locality), "Locality Missing");
                        Assert(photo.Location.Places.ContainsKey(PlaceType.Region), "Region Missing");
                        Assert(photo.Location.Places.ContainsKey(PlaceType.Country), "Country Missing");

                        Place neighborhood = photo.Location.Places[PlaceType.Neighbourhood];
                        AssertEqual(photo.Location.PlaceId, neighborhood.Id, "Neighbourhood Place Id Does Not Match");
                        AssertEqual(photo.Location.WhereOnEarthId, neighborhood.WhereOnEarthId, "Neighbourhood Where On Earth Id Does Not Match");

                        AssertEqual("North Bondi", neighborhood.Name, "Neighbourhood Name Does Not Match");
                        AssertEqual("Sydney", photo.Location.Places[PlaceType.Locality].Name, "Locality Name Does Not Match");
                        AssertEqual("New South Wales", photo.Location.Places[PlaceType.Region].Name, "Region Name Does Not Match");
                        AssertEqual("Australia", photo.Location.Places[PlaceType.Country].Name, "Country Name Does Not Match");

                        Assert(photo.LocationPermissions.IsPublic, "Location Permissions Incorrect");
                        Assert(!photo.LocationPermissions.IsContact, "Location Permissions Incorrect");
                        Assert(!photo.LocationPermissions.IsFriend, "Location Permissions Incorrect");
                        Assert(!photo.LocationPermissions.IsFamily, "Location Permissions Incorrect");

                        AssertEqual(1, photo.Urls.Count, "Incorrect Number of URLs");
                        AssertEqual("photopage", photo.Urls[0].Type, "URL Type Does Not Match");
                        AssertEqual("http://www.flickr.com/photos/74579776@N00/4627858959/", photo.Urls[0].Value, "URL Does Not Match");



                        Dispatcher.BeginInvoke(this.OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(this.OnError), err);
                    }
                }, null);

        }

        private void CheckTag(Tag t, String tagId, String author, String rawValue, String value)
        {

            AssertEqual(tagId, t.Id, "Tag Id Does Not Match");
            AssertEqual(author, t.AuthorId, "Tag Author Does Not Match");
            AssertEqual(rawValue, t.RawValue, "Tag Raw Value Does Not Match");
            AssertEqual(value, t.Value, "Tag Value Does Not Match");
            //Assert(!t.IsMachineTag, "Tag.IsMachineTag is Incorrect");
        }

        public void PhotoGetExifTest(Flickr channel)
        {
            channel.BeginPhotos_GetExif(4627858959,
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<ExifTagCollection> response = channel.EndPhotos_GetExif(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        Dispatcher.BeginInvoke(this.OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(this.OnError), err);
                    }
                }, null);

        }

        public void PhotoGetContextTest(Flickr channel)
        {
            channel.BeginPhotos_GetContext(4627858959,
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<PhotoContext> response = channel.EndPhotos_GetContext(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        AssertEqual(177, response.Element.Count, "Count Did Not Match");
                        AssertNotNull(response.Element.PreviousPhoto, "Previous Photo Missing");
                        AssertNotNull(response.Element.NextPhoto, "Next Photo Missing");

                        ValidatedRelatedPhoto(response.Element.PreviousPhoto, "Previous", 4628419120, "e3691a3585", "Shortest Surf Spot Ever Near Tamarama", "/photos/74579776@N00/4628419120/in/photostream/");
                        ValidatedRelatedPhoto(response.Element.NextPhoto, "Next", 4628812380, "b7d57c5a18", "Some Flirting", "/photos/74579776@N00/4628812380/in/photostream/");

                        Dispatcher.BeginInvoke(this.OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(this.OnError), err);
                    }
                }, null);

        }

        private void ValidatedRelatedPhoto(RelatedPhoto photo, String rel, Int64 id, String secret, String title, String url)
        {
            AssertEqual(id, photo.Id, rel + " Photo Id Does Not Match");
            AssertEqual(secret, photo.Secret, rel + " Photo Secret Does Not Match");
            Assert(photo.Server > 0, rel + " Photo Server Not Valid");
            AssertEqual(title, photo.Title, true, rel + " Photo Title Does Not Match");
            AssertEqual(url, photo.Url, true, rel + " Photo Url Does Not Match");
            Assert(!String.IsNullOrEmpty(photo.ThumbnailUrl) && photo.ThumbnailUrl.StartsWith("http"), rel + " Photo Thumbnail Url is Invalid");
            AssertEqual("photo", photo.MediaType, rel + " Photo Media Does Not Match");
        }

        public void PhotoGetRecentTest(Flickr channel)
        {
            channel.BeginPhotos_GetRecent(
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<PhotoCollection> response = channel.EndPhotos_GetRecent(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        Dispatcher.BeginInvoke(this.OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(this.OnError), err);
                    }
                }, null);
        }

        public void PhotosetGetListTest(Flickr channel)
        {
            channel.BeginPhotosets_GetPhotosets("74579776@N00",
                delegate(IAsyncResult result)
                {
                    try
                    {
                        FlickrResponse<PhotosetCollection> response = channel.EndPhotosets_GetPhotosets(result);

                        AssertNotNull(response.Element, "Response Element Null");
                        AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                        Dispatcher.BeginInvoke(this.OnSuccess);
                    }
                    catch (Exception err)
                    {
                        Dispatcher.BeginInvoke(new Action<Exception>(this.OnError), err);
                    }
                }, null);
        }

        public void PhotosetGetInfoTest(Flickr channel)
        {
            channel.BeginPhotosets_GetPhotosetInfo(72157623937884091,
                delegate(IAsyncResult result)
                {
                    FlickrResponse<PhotosetInfo> response = channel.EndPhotosets_GetPhotosetInfo(result);

                    AssertNotNull(response.Element, "Response Element Null");
                    AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                    Dispatcher.BeginInvoke(this.OnSuccess);
                }, null);
        }

        public void PhotosetGetPhotosTest(Flickr channel)
        {
            channel.BeginPhotosets_GetPhotos(72157623937884091,
                delegate(IAsyncResult result)
                {
                    FlickrResponse<Photoset> response = channel.EndPhotosets_GetPhotos(result);

                    AssertNotNull(response.Element, "Response Element Null");
                    AssertEqual(ResponseStatus.Ok, response.Status, "Response Status Incorrect");

                    Dispatcher.BeginInvoke(this.OnSuccess);
                }, null);
        }

        private void CancelTestButton_Click(Object sender, RoutedEventArgs e)
        {
            this.m_Cancel = true;
        }
    }
}
